package com.cy.pj.sys.service.aspect;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @Aspect 注解描述的类型为spring中的一个切面类型，
 * 此类型的对象我们称之为切面对象.这样的对象中的封装
 * 是扩展业务逻辑(服务增益)的实现，它通常会包含两部
 * 分内容的定义(切入点和通知)
 *
 * 1)切入点:切入拓展业务的点的集合
 * 2)通知：封装的是拓展业务逻辑的实现(日志记录，缓存，权限，事务，异步，。。。。)
 *
 */
@Slf4j
@Aspect
@Component
public class SysLogAspect {//FAQ？此类型的对象谁来调用？代理对象
    /**
     * @Pointcut 注解用于描述方法，定义切入点。
     * bean(spring容器中bean的名字)为切入点表达式中的一种定义切入点的方式
     * 在当前切入点的定义中，表示名字为sysUserServiceImpl的bean对象中，所有方法的集合
     * 为拓展业务的切入点。也就是说sysUserServiceImpl这个bean对象中的任意一个方法执行
     * 时，都要进行功能拓展(例如记录日志)。
     */
    //@Pointcut("bean(sysUserServiceImpl)")
    //@annotation方式的切入点表达式定义(表示由此注解描述的方法为切入点方法)
    @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
    public void doLog(){}//方法内部不需要写任何代码，只是用于承载注解@PointCut


    /**
     * @Around 注解描述的方法为一个业务拓展方法，此方法在aop应用中通常称之为环绕通知.
     * 此注解内部value属性的值为切入点，这些切入点方法执行时，会执行@Around注解描述
     * 的方法。在方法内部进行拓展业务逻辑的实现。
     * @param joinPoint 表示连接点，@Around注解描述的方法参数必须为ProceedingJoinPoint类型，
     *                  这个连接点对应的是切入点集合中某个正在执行的目标方法
     * @return 目标方法的返回值，@Around注解描述的方法返回值必须为object类型。
     * @throws Throwable 在执行业务的过程中可能出现的异常
     */
    @Around("doLog()")
    public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable{
        long t1=System.currentTimeMillis();
        log.info("start {}",t1);
        try {
            Object result = joinPoint.proceed();//执行目标业务方法
            long t2 = System.currentTimeMillis();
            log.info("end {}", t2);
            //保存用户正常行为日志(谁在什么时间，执行了什么操作，访问了什么方法，传递了什么参数，执行时长是多少)
            //saveUserLog(joinPoint,t2-t1)//--获取日志，调用日志service，调用日志dao，将数据写入到数据库
            return result;//这个值返回给谁，代理对象
        }catch (Throwable e){
            log.error("error {}",e.getMessage());
            throw e;
        }
    }
    @Autowired
    private SysLogService sysLogService;
    private void savaUserLog(ProceedingJoinPoint joinPoint,long time){
        //1.获取用户行为日志
        String ip="192.168.1.111";
        String username="admin";
        String operation="";
        String method="";
        String params="";
        //2.封装用户行为日志
        SysLog log=new SysLog();
        log.setIp(ip);
        log.setUsername(username);
        log.setOperation(operation);
        log.setMethod(method);
        log.setParams(params);
        log.setTime(time);
        log.setCreatedTime(new Date());
        //3.将日志写入到数据库
        sysLogService.saveObject(log);
    }
}
